package com.tlgen.orm.components.rest;

import com.google.common.collect.Maps;
import com.tlgen.orm.components.rest.model.RequestMappingClasses;
import com.tlgen.orm.components.rest.model.RequestMethodAndUrl;
import com.tlgen.orm.param.FieldParam;
import com.tlgen.orm.support.chain.ExecutionPipeline;
import com.tlgen.orm.support.chain.ServiceImplStage;
import com.tlgen.orm.support.chain.SqlExecutionStage;
import com.tlgen.orm.utils.ClassesUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.DispatcherType;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.List;
import java.util.Map;
import java.util.Objects;

import static com.tlgen.orm.constant.HttpConstant.*;
import static com.tlgen.orm.utils.http.HttpUtils.*;

@Slf4j
public class RestInterceptor implements HandlerInterceptor {

    // 自己实现了 HandlerInterceptor, 没有进入 Spring 的 MVC, 而是自己处理了,
    // 不会触发 Spring 的动态代理, 所以接口对应的 RestProxy 不会被执行,
    // 当接口被其他类注入引用的时候, 才会触发 Spring 的动态代理, 才会执行 RestProxy
    // 实现 MV 架构和 SQL-XML 的调用, 需要同时关注 HandlerInterceptor 和 RestProxy 的实现
    // 两者不会同时触发, 相互独立
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        // 只在首次请求时处理
        if (request.getDispatcherType() != DispatcherType.REQUEST) {
            return true; // 放行错误、包含等其他类型的调度
        }
        // 保存原始请求路径
        request.setAttribute(ORIGINAL_REQUEST_URI, request.getRequestURI());
        try {
            // 获取接口 bean
            List<RequestMappingClasses> requestMappingClasses = ClassesUtils.getRequestMappingClassesWithAnnotation();
            if (CollectionUtils.isEmpty(requestMappingClasses)) return true;
            // 获取入参
            List<FieldParam> requestFieldParams = getRequestFieldParams(request);
            String originalUri = (String) request.getAttribute(ORIGINAL_REQUEST_URI);
            RequestMethodAndUrl requestMethodAndUrl = getCurrentRequestMethodAndUrl(requestMappingClasses, originalUri);
            // 缓存 content-type 减少重复调用
            final String contentType = request.getContentType();
            // 一次性计算好参数数量
            final int paramCount = requestFieldParams.size();
            if (Objects.nonNull(requestMethodAndUrl)) {
                if (Objects.isNull(contentType)) {
                    // 如果请求类型为空, 说明默认是 GET 请求
                    if (Objects.isNull(requestMethodAndUrl.getArgs()) || requestMethodAndUrl.getArgs().length == 0)
                        requestMethodAndUrl.setArgs(requestFieldParams.toArray(new Object[0]));
                } else {
                    // 设置参数
                    if (Objects.equals(APPLICATION_JSON, contentType)) {
                        requestMethodAndUrl.setBodyType(0); // 请求体类型 0.application-json 1.form-data 2.@PathVariable 3.Object 0bj
                        String requestJson = (paramCount > 0) ? requestFieldParamsToJsonString(requestFieldParams) : "{}";
                        requestMethodAndUrl.setArgs(new Object[]{requestJson});
                    } else if (contentType.contains(MULTIPART_FORM_DATA)) {
                        requestMethodAndUrl.setBodyType(1);
                        if (paramCount > 0) {
                            requestMethodAndUrl.setArgs(requestFieldParams.toArray(new Object[0]));
                        } else {
                            requestMethodAndUrl.setArgs(new Object[0]);
                        }
                    }
                }
                // 构建执行管线
                ExecutionPipeline pipeline = new ExecutionPipeline()
                        .addStage(new ServiceImplStage())  // 先执行实现类
                        .addStage(new SqlExecutionStage()); // 再执行SQL（如果有）
                // 提取参数
                Map<String, Object> paramMap = Maps.newHashMap();
                for (FieldParam param : requestFieldParams) {
                    paramMap.put(param.getName(), param.getValue());
                }
                // 执行管线
                Object result = pipeline.execute(request, requestMethodAndUrl, paramMap);
                // 返回结果
                writeResponse(response, result);
                return false;
            } else {
                // 如果不是 restful, 但有 @SQL 注解, 这个不做处理, 由 RestProxy 处理; 如果没有 @SQL 注解, 则也不处理
                return true;
            }
        } catch (Exception e) {
            log.error("RestInterceptor error:", e);
        }
        return true;
    }

}
